/*********************************************************************************************************
 *  ------------------------------------------------------------------------------------------------------
 *  file description
 *  ------------------------------------------------------------------------------------------------------
 *         \file  cpul.c
 *         \unit  cpul
 *        \brief  This is a simple cpuload module for C language
 *       \author  Lamdonn
 *      \version  v1.0.0
 *      \license  GPL-2.0
 *    \copyright  Copyright (C) 2023 Lamdonn.
 ********************************************************************************************************/
#include "cpul.h"
#include <stdlib.h>
#include <string.h>

// Buffer for CRC calculation, initialized with some values
uint8_t calbuffer[CPUL_GEN_BSIZE] = {'A', 'B', 0};

/**
 * \brief Adjust the refined load based on current and target loads.
 * 
 * This function controls the refined load adjustment by comparing
 * the current load with the target load and modifying the refined
 * load accordingly.
 * 
 * \param[in] cpul: Pointer to the CPUL instance.
 * \return Returns the adjusted refined load value.
 */
static uint32_t controller(CPUL *cpul)
{
    // If the current load is less than the target load
    if (cpul->ctrl.cload < cpul->ctrl.tload) 
    {
        // If adding resolution to refined load does not exceed its current value
        if (cpul->ctrl.refine + cpul->resolution > cpul->ctrl.refine) 
        {
            // Increase the refined load by the resolution value
            cpul->ctrl.refine += cpul->resolution;
        }
    }
    // If the current load is greater than target load + 1
    else if (cpul->ctrl.cload > (cpul->ctrl.tload + 1))
    {
        // If the refined load is greater than or equal to the resolution
        if (cpul->ctrl.refine >= cpul->resolution)
        {
            // Decrease the refined load by the resolution value
            cpul->ctrl.refine -= cpul->resolution;
        }
    } 
    
    return cpul->ctrl.refine; // Return the adjusted refined load
}

/**
 * \brief Calculate CRC8 checksum.
 * 
 * This function computes the CRC8 checksum for a given data buffer.
 * It iterates through each byte of data and processes it to generate
 * the CRC value.
 * 
 * \param[in] data: Pointer to the data buffer.
 * \param[in] len: Length of the data buffer.
 * \return Returns the calculated CRC8 value.
 */
static uint8_t crc8(uint8_t *data, uint32_t len)
{
    uint8_t i; // Loop index for bit processing
    uint8_t crc = 0; // Initialize CRC value to 0
    while (len--) // Process each byte in the data buffer
    {
        crc ^= *data++; // XOR the current byte with the CRC value
        for (i = 0; i < 8; i++) // Process each bit of the byte
        {
            if (crc & 0x80) // If the highest bit of CRC is set
                crc = (crc << 1) ^ 0x07; // Shift left and XOR with the polynomial
            else 
                crc <<= 1; // Just shift left if the highest bit is not set
        }
    }
    return crc; // Return the calculated CRC8 value
}

/**
 * \brief Update the granularity of the load control.
 * 
 * This function updates the calbuffer values based on the refined load
 * using CRC8 checksum for each iteration specified by the input parameter.
 * 
 * \param[in] input: Number of iterations to update the buffer.
 * \return none.
 */
static void granularity(uint32_t input)
{
    // Loop through the specified number of iterations
    for (uint32_t i = 0; i < input; i++)
    {
        // Update calbuffer with a CRC8 value calculated from the current buffer
        calbuffer[calbuffer[0] % sizeof(calbuffer)] = crc8(calbuffer, sizeof(calbuffer));
    }
}

/**
 * \brief Initialize the CPUL instance.
 * 
 * This function initializes the CPUL structure to default values
 * and checks for valid pointers.
 * 
 * \param[in] cpul: Pointer to the CPUL instance.
 * \return Returns CPUL_E_OK on success, otherwise an error code.
 */
int cpul_init(CPUL *cpul)
{
    // Check if the cpul pointer is valid
    if (!cpul) return CPUL_E_INVALID; 
    // Check if the raw function pointer is valid
    if (!cpul->raw) return CPUL_E_RAW; 

    cpul->ctrl.cload = 0xFFFF;              // Set current load to untested value
    cpul->ctrl.tload = 0;                   // Default target load to zero (no load increase)
    cpul->ctrl.refine = 0;                  // Initialize refined load

    return CPUL_E_OK; // Initialization successful
}

/**
 * \brief Get the current CPU load.
 * 
 * This function retrieves the current load from the CPUL instance
 * and checks for validity of parameters and state.
 * 
 * \param[in] cpul: Pointer to the CPUL instance.
 * \param[out] load: Pointer to store the current load value.
 * \return Returns CPUL_E_OK on success, otherwise an error code.
 */
int cpul_get(CPUL *cpul, uint16_t *load)
{
    // Check if the cpul pointer is valid
    if (!cpul) return CPUL_E_INVALID; 
    // Check if the raw function pointer is valid
    if (!cpul->raw) return CPUL_E_RAW; 
    // Check if the load parameter is valid
    if (!load) return CPUL_E_PARA; 

    // If current load is still untested, return an error
    if (cpul->ctrl.cload == 0xFFFF) return CPUL_E_NTEST; 
    // Check if the current load exceeds the maximum allowed value
    if (cpul->ctrl.cload > 10000) return CPUL_E_OVER; 

    *load = cpul->ctrl.cload; // Retrieve the current load value

    return CPUL_E_OK; // Success
}

/**
 * \brief Set the target CPU load.
 * 
 * This function sets the target load for the CPUL instance and checks for valid parameters.
 * 
 * \param[in] cpul: Pointer to the CPUL instance.
 * \param[in] load: Target load value to set.
 * \return Returns CPUL_E_OK on success, otherwise an error code.
 */
int cpul_set(CPUL *cpul, uint16_t load)
{
    // Check if the cpul pointer is valid
    if (!cpul) return CPUL_E_INVALID; 
    // Check if the raw function pointer is valid
    if (!cpul->raw) return CPUL_E_RAW; 
    // Check if the load parameter is zero
    if (!load) return CPUL_E_PARA; 

    // Check if the load exceeds the maximum allowed value
    if (load > 10000) return CPUL_E_OVER; 

    cpul->ctrl.tload = load; // Set the target load

    return CPUL_E_OK; // Success
}

/**
 * \brief Execute a CPUL management task.
 * 
 * This function executes a task to manage the CPU load based on
 * the current and target load values.
 * 
 * \param[in] cpul: Pointer to the CPUL instance.
 * \return Returns CPUL_E_OK on success, otherwise an error code.
 */
int cpul_task(CPUL *cpul)
{
    // Check if the cpul pointer is valid
    if (!cpul) return CPUL_E_INVALID; 
    // Check if the raw function pointer is valid
    if (!cpul->raw) return CPUL_E_RAW; 

    // Call the raw function to get the current load value
    cpul->ctrl.cload = cpul->raw(cpul->coreid); 

    // If the target load is not zero, perform load management
    if (cpul->ctrl.tload != 0)
    {
        // Adjust the refined load based on current and target load
        cpul->ctrl.refine = controller(cpul); 
        // Update granularity based on the refined load
        granularity(cpul->ctrl.refine); 
    }

    return CPUL_E_OK; // Success
}
